llama.cpp: optimizaciones que siguen sorprendiendo

Engranajes de reloj precisos representando mecánica optimizada interna

El proyecto llama.cpp, creado por Georgi Gerganov, se ha convertido en la pieza sobre la que se apoya casi todo el ecosistema de modelos de lenguaje en local. Ollama, LM Studio, Jan, Msty y decenas de herramientas son, en el fondo, envoltorios alrededor de esta biblioteca en C++. Durante 2024 el ritmo de desarrollo ha sido vertiginoso: decodificación especulativa, inferencia distribuida, nuevos backends de GPU y un formato GGUF por fin estable. Merece la pena mirar qué ha cambiado y por qué sigue interesando conocer la herramienta directamente.

Qué es y por qué importa

llama.cpp es una biblioteca de inferencia escrita en C++ puro, sin apenas dependencias externas, que compila en cualquier sitio donde haya un compilador decente. Incluye una interfaz de línea de comandos, un servidor compatible con la API de OpenAI y soporte para los principales aceleradores. Su formato nativo, GGUF, es hoy la manera canónica de distribuir pesos cuantizados: un archivo autocontenido con metadatos, tokenizador y tensores en precisión reducida.

El motivo de su éxito es sencillo. Frente a vLLM, que brilla en producción con multi-GPU y procesamiento por lotes agresivo, llama.cpp se centra en el caso individual: un usuario, una máquina, sin dependencias de Python. Frente a Ollama, que prioriza la experiencia sobre el control, expone cada botón. Y frente a MLX de Apple, mantiene la portabilidad como principio no negociable. En una Raspberry Pi 5 arranca con modelos pequeños; en un Mac Studio con 128 GB mueve un 70B cuantizado sin despeinarse.

Las grandes novedades de 2024

La decodificación especulativa es probablemente la mejora más tangible del año. Un modelo pequeño y rápido, el borrador, genera varios tokens por adelantado; el grande los verifica en paralelo y acepta los que coinciden con su propia predicción. Cuando el borrador acierta, se multiplican los tokens útiles por cada pasada del modelo grande, con aceleraciones de dos o tres veces sin pérdida de calidad. Exige elegir bien la pareja —normalmente un modelo de la misma familia pero mucho más ligero—, pero cuando cuaja el efecto es notable.

La segunda novedad es el servidor RPC, que permite repartir las capas del modelo entre varias máquinas conectadas por red. No sustituye a un sistema multi-GPU real, pero convierte a tres portátiles modestos en una plataforma capaz de ejecutar modelos que ninguno tragaría por separado. Es la versión artesanal del sharding, pensada para laboratorios caseros más que para producción.

Por último, 2024 consolidó el uso de herramientas con modelos compatibles (Llama 3.1 en adelante): gramáticas GBNF para forzar salidas válidas, plantillas Jinja y generación estructurada en JSON con garantías sintácticas, similar a lo que ofrecen Outlines o Guidance.

Backends y compilación

La matriz de backends es hoy sorprendentemente completa. En CPU se aprovechan AVX2, AVX-512 y NEON según la arquitectura; para GPU hay rutas dedicadas a CUDA en NVIDIA, Metal en Apple Silicon, ROCm en AMD, SYCL en gráficas Intel y Vulkan u OpenCL como alternativas multiplataforma. Activar cada backend es cuestión de una variable de entorno al compilar —LLAMA_METAL=1, LLAMA_CUDA=1, LLAMA_VULKAN=1 y sus variantes—, y la elección depende del objetivo: CUDA ofrece el mayor rendimiento bruto, Metal gana en eficiencia energética, Vulkan facilita soporte transversal y CPU sigue siendo el último refugio cuando el hardware no acompaña.

Cuantización y formato GGUF

La cuantización es donde llama.cpp marca más diferencias. GGUF admite todo el espectro, de F32 y F16 hasta Q2_K, pero el abanico útil se reduce a tres piezas. Q8_0 es prácticamente indistinguible del modelo original y sirve de referencia para medir pérdida de calidad. Q5_K_M ofrece un equilibrio difícil de batir: uno o dos por ciento menos de precisión en pruebas reales, la mitad del tamaño que Q8 y margen para correr un 13B en un portátil decente. Q4_K_M es el punto dulce para modelos grandes: un 70B cabe holgado en 48 GB de VRAM o en un Mac con memoria unificada generosa, y la degradación es aceptable para la mayoría de tareas.

Por debajo, Q3_K_M y Q2_K empiezan a notarse: razonamientos frágiles, más alucinaciones, errores sutiles. Los cuantizados de la familia IQ —IQ4_NL, IQ3_XXS— aplican una estrategia basada en la importancia de cada peso y a igualdad de tamaño suelen rendir mejor que sus equivalentes clásicos.

Modo servidor y bindings

llama-server arranca un endpoint compatible con la API de OpenAI: cualquier cliente pensado para GPT funciona sin cambiar una línea. Esto es lo que Ollama empaqueta, pero llegar por tu cuenta al mismo punto cuesta un solo comando y abre configuraciones que Ollama esconde: tamaños de contexto personalizados, parámetros de muestreo concretos, capas delegadas a GPU con -ngl o adaptadores LoRA aplicados en caliente.

./llama-server -m model.gguf --host 0.0.0.0 --port 8080 -ngl 99

Para quien prefiera Python, el paquete llama-cpp-python expone la misma biblioteca con interfaz idiomática y servidor propio. Es la vía habitual para integrarlo en LangChain, LlamaIndex o cualquier aplicación Python.

Apple Silicon y el caso Mac

Los Mac se han convertido en el hardware de referencia para modelos locales grandes. La memoria unificada elimina el cuello de botella entre RAM y VRAM: un M3 Max con 128 GB puede cargar un 70B cuantizado a Q4 con contexto amplio sin trasiego de buses. El backend Metal está tan pulido que en eficiencia por vatio supera a muchas configuraciones NVIDIA de gama media. El resultado son 60-80 tokens por segundo en Llama 3 8B Q4, frente a los 150+ de una RTX 4090 pero con una fracción del consumo.

Cuándo ir directo y cuándo usar Ollama

Ollama es suficiente para el 90% de los casos: descarga, empaqueta, gestiona modelos y expone una API limpia. Llamar a llama.cpp directamente compensa cuando se necesita exprimir hardware poco común, embeber el binario en una aplicación sin dependencias Python, experimentar con flags de muestreo o ejecutar el proyecto semanas antes de que Ollama adopte sus novedades. También cuando hace falta un binario estático en entornos sin conexión.

Para producción con varios usuarios concurrentes, en cambio, la recomendación honesta es mirar a vLLM: llama.cpp está optimizado para un único flujo de inferencia, y la paralelización multi-GPU sigue siendo más engorrosa que en alternativas especializadas.

Conclusión

Lo fascinante de llama.cpp es que, siendo el motor invisible de casi todo, mantiene una velocidad de iteración que ningún envoltorio puede seguir de cerca. Los commits son diarios, los backends se renuevan en semanas y GGUF ha resistido la presión de ser a la vez lingua franca del ecosistema y banco de pruebas para nuevas ideas de cuantización.

Esa tensión entre estabilidad y vanguardia es lo que lo hace valioso. Ollama y LM Studio existen porque la mayoría no quiere lidiar con flags de compilación ni elegir entre siete variantes de Q4, pero su existencia no vuelve prescindible al motor que llevan dentro. Al contrario: cuanto más maduro es el envoltorio, más evidente resulta que cualquier mejora seria en inferencia local pasa antes por el repositorio de Gerganov.

La posición sensata es tratarlo como un compilador o un kernel: no hace falta recompilarlo cada semana, pero conviene entender qué ofrece y cómo se comporta bajo carga. Cuando llegue el día en que Ollama no soporte el backend que necesitas, el modelo que quieres probar o el flag que te importa, saber bajar un nivel deja de ser opcional. Y ese día, casi siempre, llega antes de lo previsto.

Entradas relacionadas